﻿using System;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Windows;
using System.Windows.Media;
using System.Xml.Linq;


namespace bATR.Config
{
	public class bATRConfigLogic
	{
		public bATRConfigLogic()
		{
			if (string.IsNullOrEmpty(ConfigurationManager.AppSettings["batrConfigFile"].ToString()))
				throw new ApplicationException("appsetting 'batrConfigFile' not found!");

			this.ConfigFile = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["batrConfigFile"].ToString());
			this.LoadConfig();
		}

		public string ConfigFile { get; set; }

		public bATRConfig Config { get; set; }

		public bATRStyle GetStyle(string themeName, string styleName)
		{
			bATRStyle style = this.Config.Themes.Where(t => t.Name == themeName).FirstOrDefault().Styles.Where(s => s.Name == styleName).FirstOrDefault<bATRStyle>();
			if (style == null)
				throw new ApplicationException("style '" + styleName + "' could not be found for theme '" + themeName + "'");

			return style;
		}

		private void LoadConfig()
		{
			XDocument configFile = XDocument.Load(this.ConfigFile);

			if (configFile.Descendants("admin").Count() == 0)
				throw new ApplicationException("admin section missing");

			if (configFile.Descendants("theme").Count() == 0)
				throw new ApplicationException("theme section missing");

			bATRConfig result = (from c in configFile.Elements("bATRConfig")
								  select new bATRConfig
								  {
									  CacheExpiration = parseInt(c.Attribute("cacheExpiration"), 300),
									  CachedItemsLimit = parseInt(c.Attribute("cachedItemsLimit"), 100),
									  CachingEnabled = parseBool(c.Attribute("caching"), true),
									  ClientCacheExpiration = parseInt(c.Attribute("clientCacheExpiration"), 7),
									  EnablebATRHeaderInfo = parseBool(c.Attribute("enablebATRHeaderInfo"), true),
									  EnginePath = parseUri(c.Attribute("bATREnginePath")),
									  FontPath = new Uri(parseUri(c.Attribute("bATREnginePath")), "fonts").AbsoluteUri + "/#",
									  ImageExtension = ".png",
									  ImagePath = parseRequiredString(c.Attribute("imagePath")),
									  ReplaceImagesOnPrint = parseBool(c.Attribute("replaceImagesOnPrint"), true),
									  Admin = new bATRAdmin
									  {
										  Username = parseRequiredString(c.Descendants("admin").Descendants("username").First()),
										  Password = parseRequiredString(c.Descendants("admin").Descendants("password").First()),
									  },
									  Themes = (from t in c.Descendants("theme")
												select new bATRTheme
												{
													Name = t.Attribute("name").Value,
													Styles = (from s in t.Descendants("style")
															  select new bATRStyle
															  {
																  CharacterSpacing = parseDouble(s.Attribute("characterSpacing"), 0),
																  CharacterSpacingSpaceSize = parseDouble(s.Attribute("spaceSize"), 5),
																  FontColor = parseColor(s.Attribute("fontColor"), Colors.Black),
																  BackgroundColor = parseColor(s.Attribute("bgColor"), Colors.Black),
																  FontName = parseString(s.Attribute("fontName"), parseRequiredString(t.Attribute("defaultFontName"))),
																  FontSize = parseDouble(s.Attribute("fontSize"), 10.2),
																  FontStretch = parseFontStretch(s.Attribute("fontStretch"), parseFontStretch(t.Attribute("defaultFontStretch"), FontStretches.Normal)),
																  FontStyle = parseFontStyle(s.Attribute("fontStyle"), parseFontStyle(t.Attribute("defaultFontStyle"), FontStyles.Normal)),
																  FontWeight = parseFontWeight(s.Attribute("fontWeight"), parseFontWeight(t.Attribute("defaultFontWeight"), FontWeights.Normal)),
																  HeaderHtmlTag = parseString(s.Attribute("headerHtmlTag"), string.Empty),
																  InlineImage = parseString(s.Attribute("inlineImage"), string.Empty),
																  InlineImageRect = parseRect(s.Attribute("inlineImageRect"), new Rect()),
																  LineHeight = parseDouble(s.Attribute("lineHeight"), 0),
																  Name = parseRequiredString(s.Attribute("name")),
																  TextStartPoint = parsePoint(s.Attribute("textStartPoint"),new Point(0, 0)),
																  TextToLower = parseBool(s.Attribute("textToLower"), false),
																  TextToUpper = parseBool(s.Attribute("textToUpper"), false),
																  RenderedImageWidth = parseInt(s.Attribute("renderedImageWidth"), 0),
																  RenderedImageHeight = parseInt(s.Attribute("renderedImageHeight"), 0),
																  GradientColor = parseColor(s.Attribute("gradientColor")),
																  GradientAngle = parseDouble(s.Attribute("gradientAngle")),
																  TextDecorationPen = parseDecorationPen(s),
																  HasTextDecorationPen = hasDecorationPen,
																  TextDecorationPenOffset = parseDouble(s.Attribute("decorationOffSet"), 3D),
																  TextDecorationLocation = parseTextDecorationLocation(s.Attribute("decorationStyle"))
															  }
															  ).ToList<bATRStyle>()
												}
												).ToList<bATRTheme>()
									  
								  }
								).FirstOrDefault<bATRConfig>();

			this.Config = result;
		}


		#region parsers for usage in Linq query

		/// <summary>
		/// Parses textdecoration from Attribute or returns Underline if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private TextDecorationLocation parseTextDecorationLocation(XAttribute valueToParse)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return TextDecorationLocation.Underline;

			try
			{
				return (TextDecorationLocation)Enum.Parse(typeof(TextDecorationLocation), valueToParse.Value, true);
			}
			catch
			{
				return TextDecorationLocation.Underline;
			}
		}


		private bool hasDecorationPen = false;

		/// <summary>
		/// Parses a Pen from xElement or empty Pen if failed
		/// </summary>
		/// <param name="element"></param>
		/// <returns></returns>
		private Pen parseDecorationPen(XElement element)
		{
			try
			{
				if (element.Attribute("decorationColor") != null && element.Attribute("decorationThickness") != null)
				{
					Pen pen = new Pen(parseBrush(element.Attribute("decorationColor")), (double)parseDouble(element.Attribute("decorationThickness")));
					
					//if (element.Attribute
					if (parseColor(element.Attribute("decorationColor")) != null 
						&& parseColor(element.Attribute("decorationGradient")) != null 
						&& parseDouble(element.Attribute("decorationGardientAngle")) != null)
					{
						pen.Brush = new LinearGradientBrush((Color)parseColor(element.Attribute("decorationColor"))
							, (Color)parseColor(element.Attribute("decorationGradient"))
							, (double)parseDouble(element.Attribute("decorationGardientAngle")));
					}
					hasDecorationPen = true;
					return pen;
				}
				hasDecorationPen = false;
				return new Pen();
			}
			catch
			{
				hasDecorationPen = false;
				return new Pen();
			}
		}


		/// <summary>
		/// Parses attribute to URI or throws exception if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private Uri parseUri(XAttribute valueToParse)
		{
			try
			{
				if (valueToParse.Value.StartsWith("~"))
					return new Uri(HttpContext.Current.Server.MapPath(valueToParse.Value));

				return new Uri(valueToParse.Value);
			}
			catch
			{
				throw new ApplicationException("Wrong or missing value for attribute: " + valueToParse.Name + ".");
			}
		}


		/// <summary>
		/// Parses attribute to string or returns default if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private string parseString(XAttribute valueToParse, string defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			return valueToParse.Value;
		}


		/// <summary>
		/// Parses element to string
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private string parseRequiredString(XElement valueToParse)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				throw new ApplicationException("string attribute '" + valueToParse.Name + "' missing or empty");

			return valueToParse.Value;
		}


		/// <summary>
		/// Parses attribute to string
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private string parseRequiredString(XAttribute valueToParse)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				throw new ApplicationException("string attribute '" + valueToParse.Name + "' missing or empty");

			return valueToParse.Value;
		}


		/// <summary>
		/// Parses attribute to Point or returns default if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private Point parsePoint(XAttribute valueToParse, Point defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				PointConverter pc = new PointConverter();
				return (Point)pc.ConvertFromString(valueToParse.Value.Replace(";", ","));
			}
			catch
			{
				return defaultValue;
			}
		}


		/// <summary>
		/// Parses attribute to Rect or returns default if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private Rect parseRect(XAttribute valueToParse, Rect defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				RectConverter rc = new RectConverter();
				return (Rect)rc.ConvertFromString(valueToParse.Value.Replace(";", ","));
			}
			catch
			{
				return defaultValue;
			}
		}


		/// <summary>
		/// Parses attribute to FontWeight or returns default if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private FontWeight parseFontWeight(XAttribute valueToParse, FontWeight defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				FontWeightConverter fwc = new FontWeightConverter();
				return (FontWeight)fwc.ConvertFromString(valueToParse.Value);
			}
			catch
			{
				return defaultValue;
			}
		}


		/// <summary>
		/// Parses attribute to FontStyle or returns default if parse failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private FontStyle parseFontStyle(XAttribute valueToParse, FontStyle defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				FontStyleConverter fsc = new FontStyleConverter();
				return (FontStyle)fsc.ConvertFromString(valueToParse.Value);
			}
			catch
			{
				return defaultValue;
			}
		}
		

		/// <summary>
		/// Parses XAttribute to FontStretch or return default value if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private FontStretch parseFontStretch(XAttribute valueToParse, FontStretch defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				FontStretchConverter fsc = new FontStretchConverter();
				return (FontStretch)fsc.ConvertFromString(valueToParse.Value);
			}
			catch
			{
				return defaultValue;
			}
		}


		/// <summary>
		/// Parses Attribute to color or returns defualt value if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private Color parseColor(XAttribute valueToParse, Color defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				return (Color)ColorConverter.ConvertFromString(valueToParse.Value);
			}
			catch
			{
				return defaultValue;
			}
		}
		

		/// <summary>
		/// Parses attribute to Color or returns null of failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private Color? parseColor(XAttribute valueToParse)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return null;

			try
			{
				return (Color)ColorConverter.ConvertFromString(valueToParse.Value);
			}
			catch
			{
				return null;
			}
		}


		/// <summary>
		/// Parses XAttribute to Brush or returns null if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private Brush parseBrush(XAttribute valueToParse)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return null;

			try
			{
				BrushConverter bc = new BrushConverter();
				return (Brush)bc.ConvertFromString(valueToParse.Value);
			}
			catch
			{
				return null;
			}
		}


		/// <summary>
		/// Parses XAttribute to int or returns default if parse failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private int parseInt(XAttribute valueToParse, int defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				return int.Parse(valueToParse.Value);
			}
			catch
			{
				return defaultValue;
			}
		}


		/// <summary>
		/// Parses the xAttribute to double or returns default if failed
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private double parseDouble(XAttribute valueToParse, double defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			try
			{
				return double.Parse(valueToParse.Value);
			}
			catch
			{
				return defaultValue;
			}
		}


		/// <summary>
		/// Parses the XAttribute to an double (or null if failed)
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <returns></returns>
		private double? parseDouble(XAttribute valueToParse)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return null;

			try
			{
				return double.Parse(valueToParse.Value);
			}
			catch
			{
				return null;
			}
		}


		/// <summary>
		/// Parses the supplied xAttribute and returns as bool (or default value if parse failed)
		/// </summary>
		/// <param name="valueToParse"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		private bool parseBool(XAttribute valueToParse, bool defaultValue)
		{
			if (valueToParse == null || valueToParse.Value.Length == 0)
				return defaultValue;

			return (valueToParse.Value.ToLower() == bool.TrueString.ToString().ToLower());
		} 

		#endregion
	}
}
